package org.dbwebx.flex.model.delegates {
	import mx.messaging.ChannelSet;
	import mx.messaging.channels.AMFChannel;
	import mx.rpc.AsyncToken;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.remoting.mxml.RemoteObject;
	import mx.rpc.remoting.mxml.Operation;

	import flash.events.EventDispatcher;
	import flash.utils.getQualifiedClassName;

	/**
	 * @author Izzet_Mustafayev
	 */
	public class AbstractDelegate extends EventDispatcher {

		public static var defaultResultHandler : Function = defaultRemoteDelegateResultHandler;
		public static var defaultFaultHandler : Function = defaultRemoteDelegateFaultHandler;
		private var _token : AsyncToken;

		private var _remoteObject : RemoteObject;

		private var _service : String;

		private var _resultHandler : Function;
		private var _faultHandler : Function;

		private var _executeDefaultResultHandler : Boolean;
		private var _executeDefaultFaultHandler : Boolean;

		
		public function AbstractDelegate(
            service : String,
            resultHandler : Function,
            faultHandler : Function = null,
            executeDefaultResultHandler : Boolean = true,
            executeDefaultFaultHandler : Boolean = true) {
			super();

			_service = service;
			_resultHandler = resultHandler;
			_faultHandler = faultHandler;
			_executeDefaultResultHandler = executeDefaultResultHandler;
			_executeDefaultFaultHandler = executeDefaultFaultHandler;
			//_executeDefaultInvokeHandler = executeDefaultInvokeHandler;

			_remoteObject = new RemoteObject(_service);
			_remoteObject.destination = _service;
			_remoteObject.showBusyCursor = true;
			_remoteObject.requestTimeout = 60;

			_remoteObject.addEventListener(ResultEvent.RESULT, onResult);
			_remoteObject.addEventListener(FaultEvent.FAULT, onFault);
			//define chanels
			var channel : AMFChannel = new AMFChannel("my-amf", "http://localhost:9090/flexds/messagebroker/amf");
			var channelSet : ChannelSet = new ChannelSet();
			channelSet.addChannel(channel);
			_remoteObject.channelSet = channelSet;
		}

		/**
		 * Invokes destination method.
		 * @param method the method to invoke
		 */
		protected function executeCall(method : String, args : Array = null) : AsyncToken {
			var message : String = getQualifiedClassName(this) + "->" + _service + "." + method + "(" + (args != null ? args.toString() : "") + ")";
			trace("EXECUTE CALL:", message);
			var operation : Operation = _remoteObject.getOperation(method) as Operation;
			if(null != args) {
				_token = (operation.send as Function).apply(this, args);
			}
			if(null == args) {
				_token = (operation.send as Function).apply(this);
			}
						
			return _token;
		}

		private function onResult(event : ResultEvent) : void {
			
			if(_executeDefaultResultHandler && defaultResultHandler != null) {
				if(defaultResultHandler(event)) return;
			}
			if(_resultHandler != null) {
				_resultHandler(event);
			}
		}

		protected function onFault(event : FaultEvent) : void {
			
			// TODO: should resolve exception more correctly
			// for example we define some error codes with own priorities,
			// and some of them should be recognized as server errors
			// !!! Do it in defaultFaultHandler - not here
			if (_executeDefaultFaultHandler && defaultFaultHandler != null) {
				if(defaultFaultHandler(event)) return;
			}
			if(_faultHandler != null) {
				_faultHandler(event);
			}
		}

		private static function defaultRemoteDelegateResultHandler(event : ResultEvent) : Boolean {
			
			return false;
		}

		private static function defaultRemoteDelegateFaultHandler(event : FaultEvent) : Boolean {
			
			return false;
		}

		public function get lastToken() : AsyncToken {
			return _token;
		}
	}
}
